package com.element.security.resource.filter;

import com.common.core.config.SpringApplicationContext;
import com.common.core.exception.ExceptionFactory;
import com.common.core.exception.code.SecurityErrorCode;
import com.element.security.model.ScopeGrantedAuthority;
import com.element.security.model.entity.AuthUserE;
import com.element.security.resource.config.PermitAllUrlProperties;
import com.element.security.resource.context.SecurityContext;
import com.element.security.utils.SecurityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import reactor.util.annotation.NonNull;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @auther WuJun.Zhang
 * @date 2022/3/23 下午12:03
 */
@Component
@ConditionalOnClass(GlobalSecurityFilter.class)
public class SecurityHandlerInterceptor extends HandlerInterceptorAdapter {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 在请求处理之前进行调用（Controller方法调用之前）
     */
    @Override
    public boolean preHandle(HttpServletRequest request,
                             @NonNull HttpServletResponse response,
                             @NonNull Object handler) {
        String url = request.getServletPath();
        logger.info("url={}", url);
        PermitAllUrlProperties permitAllUrlProperties = SpringApplicationContext.getContext().getBean(PermitAllUrlProperties.class);
        List<String> ignoreUrls = permitAllUrlProperties.getIgnoreUrls();
        if (permitAllUrlProperties.hasMatcher(ignoreUrls, url)) {
            return true;
        }
        OAuth2Authentication authentication = (OAuth2Authentication) SecurityUtil.getAuthentication();
        OAuth2Request auth2Request = authentication.getOAuth2Request();
        if (!CollectionUtils.isEmpty(auth2Request.getScope())) {
            AtomicBoolean isPath = new AtomicBoolean(false);
            Collection<? extends GrantedAuthority> authorities = auth2Request.getAuthorities();
            authorities.forEach(grantedAuthority -> {
                ScopeGrantedAuthority scopeGrantedAuthority = (ScopeGrantedAuthority) grantedAuthority;
                if (auth2Request.getScope().contains(scopeGrantedAuthority.getAuthority())) {
                    if (permitAllUrlProperties.hasMatcher(scopeGrantedAuthority.getApis(), url)) {
                        isPath.set(true);
                    }
                }
            });
            if (!isPath.get()) {
                throw ExceptionFactory.bizException(SecurityErrorCode.A_ACCESS_DENIED_API);
            }
        }
        AuthUserE authUser = SecurityUtil.getUser();
        if (null != authUser) {
            SecurityContext.putUser(authUser);
        }
        String appId = SecurityUtil.getAppId();
        if (StringUtils.hasText(appId)) {
            SecurityContext.putAppId(appId);
        }
        return true;
    }

    /**
     * 请求处理之后进行调用，但是在视图被渲染之前（Controller方法调用之后）
     */
    @Override
    public void postHandle(@NonNull HttpServletRequest request,
                           @NonNull HttpServletResponse response,
                           @NonNull Object handler,
                           ModelAndView modelAndView) {
        SecurityContext.removeUser();
        SecurityContext.removeAppId();
    }
}
